package com.zst.http.forward.server;

import com.zst.http.forward.configuration.ForwardServerProperties;
import com.zst.http.forward.remote.ForwardHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpVersion;
import lombok.extern.slf4j.Slf4j;

import java.text.MessageFormat;
import java.util.concurrent.CompletableFuture;

@Slf4j
public class ForwardServer {
    private Channel serverChannel;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private SimpleRequestForwardHandler sharedRequestForwardHandler;
    private ForwardServerProperties props;
    private ForwardHandler forwardHandler;

    public ForwardServer(ForwardServerProperties props, ForwardHandler forwardHandler) {
        validateForwardServerProperties(props);
        this.props = props;

        if (forwardHandler == null) {
            throw new IllegalArgumentException("ForwardHandler cannot be null");
        }
        this.forwardHandler = forwardHandler;

        sharedRequestForwardHandler = new SimpleRequestForwardHandler();

        init();
    }

    private void validateForwardServerProperties(ForwardServerProperties props) {
        if (props == null) {
            throw new IllegalArgumentException("ForwardServerProperties cannot be null");
        }
    }

    private void init() {
        if (!props.isEnable()) {
            log.info("ForwardServer is disabled");
            return;
        }

        bossGroup = new NioEventLoopGroup(props.getAcceptThreads());
        workerGroup = new NioEventLoopGroup(props.getWorkerThreads());

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_REUSEADDR, true)
                .childHandler(new childInitializer());

        try {
            serverChannel = serverBootstrap.bind(props.getPort()).sync().channel();
            serverChannel.closeFuture().addListener(future -> {
                close();
            });
            log.debug("HTTP转发服务已启动");
        } catch (Exception e) {
            log.error("Failed to start ForwardServer", e);
        }


    }

    private void close() {
        try {
            if (serverChannel != null) {
                serverChannel.close();
                serverChannel = null;
            }

            if (bossGroup != null && !bossGroup.isShutdown()) {
                bossGroup.shutdownGracefully().sync();
                bossGroup = null;
            }

            if (workerGroup != null && !workerGroup.isShutdown()) {
                workerGroup.shutdownGracefully().sync();
                workerGroup = null;
            }
        } catch (InterruptedException e) {
            log.error("Failed to stop ForwardServer", e);
        }
    }

    private class childInitializer extends ChannelInitializer<Channel> {

        @Override
        protected void initChannel(Channel ch) throws Exception {
            ch.pipeline().addLast(new HttpServerCodec())
                    .addLast(new HttpObjectAggregator(props.getMaxBodySize()))
                    .addLast(sharedRequestForwardHandler);
        }
    }

    @ChannelHandler.Sharable
    private class SimpleRequestForwardHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
            log.debug("收到请求:{}", msg.toString());
            CompletableFuture<FullHttpResponse> future = forwardHandler.doForward(msg);
            // TODO 这里没处理连接超时问题，以及创建出来的channel的释放的问题
            future.whenComplete((response, throwable) -> {
                if (throwable != null) {
                    exceptionCaught(ctx, throwable);
                    ctx.channel().close();
                } else {
                    log.debug("接受转发响应数据成功，返回响应到原始请求处,{}", response.toString());
                    response.retain();
                    ctx.writeAndFlush(response).addListener(f -> {
                        if (!f.isSuccess()) {
                            exceptionCaught(ctx, f.cause());
                            ctx.channel().close();
                        }
                    });
                }
            });
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            log.error(MessageFormat.format("转发请求时发生错误, {0}", ctx.channel().toString()), cause);

            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR);
            ctx.writeAndFlush(response);
            ctx.close();
        }
    }
}
